// addrspace.cc 
//      Routines to manage address spaces (executing user programs).
//
//      In order to run a user program, you must:
//
//      1. link with the -N -T 0 option 
//      2. run coff2noff to convert the object file to Nachos format
//              (Nachos object code format is essentially just a simpler
//              version of the UNIX executable object code format)
//      3. load the NOFF file into the Nachos file system
//              (if you haven't implemented the file system yet, you
//              don't need to do this last step)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "addrspace.h"
#include "noff.h"

#include <strings.h>		/* for bzero */

#include "synch.h"

/*
static void ReadAtVirtual(OpenFile *executable, int virtualaddr, int numBytes, int position, TranslationEntry *pageTable, unsigned numPages){
	int ret, i, j, t_numPage;
	bool ryt;
	char tampon[PageSize];

	// on verifie qu'on veut lire quelquechose
	ASSERT(!(numBytes <= 0));

	// si le nombre de page ne suffit pas
	// normalement ca n'arrive jamais
	ASSERT (!((int)numPages*PageSize < numBytes));

	// on calcule le nombre de page dont on a besoin pour stoquer les donnees lues
	t_numPage = divRoundUp(numBytes,PageSize);
	for(i = 0; i < t_numPage; i++){
		// on lit de quoi remplir une page
		ret = executable->ReadAt(tampon, PageSize, position+(PageSize*i));
		// ret contient le nombre de byte lu
		ASSERT(ret > 0);
		for(j = 0; j < ret; j++){
//			fprintf(stderr, "virtualaddr pour WriteMem: %i\n", virtualaddr);
//			fprintf(stderr, "adresse virtuelle pour WriteMem: %i\n", virtualaddr+pageTable[i].virtualPage*PageSize+j);
			ryt = machine->WriteMem(virtualaddr+(pageTable[i].virtualPage*PageSize)+j, 1, tampon[j]);
			ASSERT(ryt == true);
		}
	}

	return;
}
*/
//----------------------------------------------------------------------
// SwapHeader
//      Do little endian to big endian conversion on the bytes in the 
//      object file header, in case the file was generated on a little
//      endian machine, and we're now running on a big endian machine.
//----------------------------------------------------------------------

static void
SwapHeader (NoffHeader * noffH)
{
    noffH->noffMagic = WordToHost (noffH->noffMagic);
    noffH->code.size = WordToHost (noffH->code.size);
    noffH->code.virtualAddr = WordToHost (noffH->code.virtualAddr);
    noffH->code.inFileAddr = WordToHost (noffH->code.inFileAddr);
    noffH->initData.size = WordToHost (noffH->initData.size);
    noffH->initData.virtualAddr = WordToHost (noffH->initData.virtualAddr);
    noffH->initData.inFileAddr = WordToHost (noffH->initData.inFileAddr);
    noffH->uninitData.size = WordToHost (noffH->uninitData.size);
    noffH->uninitData.virtualAddr =
	WordToHost (noffH->uninitData.virtualAddr);
    noffH->uninitData.inFileAddr = WordToHost (noffH->uninitData.inFileAddr);
}

//----------------------------------------------------------------------
// AddrSpace::AddrSpace
//      Create an address space to run a user program.
//      Load the program from a file "executable", and set everything
//      up so that we can start executing user instructions.
//
//      Assumes that the object code file is in NOFF format.
//
//      First, set up the translation from program memory to physical 
//      memory.  For now, this is really simple (1:1), since we are
//      only uniprogramming, and we have a single unsegmented page table
//
//      "executable" is the file containing the object code to load into memory
//----------------------------------------------------------------------

AddrSpace::AddrSpace (OpenFile * executable)
{
    NoffHeader noffH;
    unsigned int i, size;
 	
    executable->ReadAt ((char *) &noffH, sizeof (noffH), 0);
    if ((noffH.noffMagic != NOFFMAGIC) &&
	(WordToHost (noffH.noffMagic) == NOFFMAGIC))
	SwapHeader (&noffH);
    ASSERT (noffH.noffMagic == NOFFMAGIC);

// how big is address space?
    size = noffH.code.size + noffH.initData.size + noffH.uninitData.size + UserStackSize;	// we need to increase the size
    // to leave room for the stack
    numPages = divRoundUp (size, PageSize);
    size = numPages * PageSize;
#ifdef CHANGED
	nombrePiles =  divRoundUp (UserStackSize, 3*PageSize);
	
	basDePile = size;
#endif //CHANGED
    ASSERT (numPages <= NumPhysPages);	// check we're not trying
    // to run anything too big --
    // at least until we have
    // virtual memory
#ifdef CHANGED	
	if (numPages > machine->frameprovider->NumAvailFrame()) {
		 DEBUG ('p', "Pas suffisamment de memoire (%d / %d)!\n",numPages, machine->frameprovider->NumAvailFrame ());
		 //return;
	}
#endif //CHANGED
    DEBUG ('a', "Initializing address space, num pages %d, size %d\n",
	   numPages, size);
// first, set up the translation 
    pageTable = new TranslationEntry[numPages];
    for (i = 0; i < numPages; i++)
      {
	  pageTable[i].virtualPage = i;	// for now, virtual page # = phys page #
#ifdef CHANGED
	pageTable[i].physicalPage = machine->frameprovider->GetEmptyFrame(FALSE);
#else
	  pageTable[i].physicalPage = i;
#endif // CHANGED
	  pageTable[i].valid = TRUE;
	  pageTable[i].use = FALSE;
	  pageTable[i].dirty = FALSE;
	  pageTable[i].readOnly = FALSE;	// if the code segment was entirely on 
	  // a separate page, we could set its 
	  // pages to be read-only
      }

// zero out the entire address space, to zero the unitialized data segment 
// and the stack segment
int j=0;
#ifndef CHANGED
    bzero (machine->mainMemory, size);
#else

    //bzero (machine->mainMemory, size);


#endif //CHANGED
	RestoreState();
// then, copy in the code and data segments into memory
    if (noffH.code.size > 0)
      {
	  DEBUG ('a', "Initializing code segment, at 0x%x, size %d\n",
		 noffH.code.virtualAddr, noffH.code.size);
#ifndef CHANGED		 
	  executable->ReadAt (&(machine->mainMemory[noffH.code.virtualAddr]),
			      noffH.code.size, noffH.code.inFileAddr);
#else
ReadAtVirtual(executable, noffH.code.virtualAddr, noffH.code.size, noffH.code.inFileAddr, pageTable, numPages);

//	  executable->ReadAt (&(machine->mainMemory[noffH.code.virtualAddr]),			      noffH.code.size, noffH.code.inFileAddr);

#endif //CHANGED			      
      }
    if (noffH.initData.size > 0)
      {
	  DEBUG ('a', "Initializing data segment, at 0x%x, size %d\n",
		 noffH.initData.virtualAddr, noffH.initData.size);
#ifndef CHANGED
	  executable->ReadAt (&
			      (machine->mainMemory
			       [noffH.initData.virtualAddr]),
			      noffH.initData.size, noffH.initData.inFileAddr);
#else
ReadAtVirtual(executable, noffH.initData.virtualAddr, noffH.initData.size, noffH.initData.inFileAddr, pageTable, numPages);
	 

#endif //CHANGED
      }
	#ifdef CHANGED
 		 threadBitMap = new BitMap(nombrePiles);
 		 threadBitMap->Mark(0);
 		
 	    
      	for (j = 0; j < nombrePiles ; j++) {
      		sem_thread[j] = (void*) new Semaphore("sem_thread",1);
      	}
      	
      	  // ((Semaphore*)(sem_thread[9]))->P();
      	
      	SemNumThreads = new Semaphore ("NumThreads avail", 1);
      	((Semaphore*)(sem_thread[0]))->P();
 	#endif

}

//----------------------------------------------------------------------
// AddrSpace::~AddrSpace
//      Dealloate an address space.  Nothing for now!
//----------------------------------------------------------------------

AddrSpace::~AddrSpace ()
{
	int i;
  // LB: Missing [] for delete
  // delete pageTable;
  delete [] pageTable;
  delete threadBitMap;
 for (i=0; i<nombrePiles; i++) delete (Semaphore *)sem_thread[i];
  delete [] sem_thread;
  for (i = 0; i < NumPhysPages; i++) {
  	machine->frameprovider->ReleaseFrame(i);
}
  
  
  // End of modification
}

//----------------------------------------------------------------------
// AddrSpace::InitRegisters
//      Set the initial values for the user-level register set.
//
//      We write these directly into the "machine" registers, so
//      that we can immediately jump to user code.  Note that these
//      will be saved/restored into the currentThread->userRegisters
//      when this thread is context switched out.
//----------------------------------------------------------------------

void
AddrSpace::InitRegisters ()
{
    int i;

    for (i = 0; i < NumTotalRegs; i++)
    machine->WriteRegister (i, 0);
   // if (i!=8)machine->WriteRegister (i, 0);
	//if (i< 8 && i>16)machine->WriteRegister (i, 0);

    // Initial program counter -- must be location of "Start"
    machine->WriteRegister (PCReg, 0);

    // Need to also tell MIPS where next instruction is, because
    // of branch delay possibility
    machine->WriteRegister (NextPCReg, 4);

    // Set the stack register to the end of the address space, where we
    // allocated the stack; but subtract off a bit, to make sure we don't
    // accidentally reference off the end!
    machine->WriteRegister (StackReg, numPages * PageSize - 16);
    DEBUG ('a', "Initializing stack register to %d\n",
	   numPages * PageSize - 16);
}

#ifdef CHANGED
void AddrSpace::do_InitRegister (int thread_id,int f,int arg){
    
   
    int FinPile;
    int i;
    
    //FinPile = UserStackSize / MaxThread * thread_id - 16 ;
    FinPile = basDePile - (thread_id * 3 * PageSize);
    
    //for (i = 0; i < NumTotalRegs; i++) machine->WriteRegister (i, 0);
    DEBUG ('a', "initialistion Pile Registre %d\n", numPages * PageSize - FinPile);
  	machine->WriteRegister (4, arg);
    machine->WriteRegister (PCReg, f);
    machine->WriteRegister (NextPCReg, f+4);
    machine->WriteRegister (StackReg, FinPile);
   // machine->WriteRegister (RetAddrReg, 12);
    
}
#endif

//----------------------------------------------------------------------
// AddrSpace::SaveState
//      On a context switch, save any machine state, specific
//      to this address space, that needs saving.
//
//      For now, nothing!
//----------------------------------------------------------------------

void
AddrSpace::SaveState ()
{
	pageTable = machine->pageTable;
	numPages = machine->pageTableSize;
}

//----------------------------------------------------------------------
// AddrSpace::RestoreState
//      On a context switch, restore the machine state so that
//      this address space can run.
//
//      For now, tell the machine where to find the page table.
//----------------------------------------------------------------------

void
AddrSpace::RestoreState ()
{
    machine->pageTable = pageTable;
    machine->pageTableSize = numPages;
}


#ifdef CHANGED

void AddrSpace::ReadAtVirtual(OpenFile *executable, int virtualaddr, int numBytes, int position, TranslationEntry *pageTable, unsigned numPages){
	int i,startfrom;
	int numByteToRead;
	int virtualPageToRead;
	int physicalPageToRead;
	int vbackup=virtualaddr;
	int nbbckp=numBytes;
	
	char * buffer =new char[numBytes];
	i=0;
	while(numBytes>0){
		startfrom=virtualaddr%PageSize;
		numByteToRead=PageSize-startfrom;
		if (numByteToRead<numBytes)numByteToRead=numBytes;
		executable->ReadAt((char *)(buffer+i),numByteToRead,position);

		position += numByteToRead;
		numBytes -= numByteToRead;
		i+=numByteToRead;
	}
	//printf("vbackup:%d, numbyte:%d >%x< (%s)\n",vbackup,nbbckp,buffer,buffer);

	if (nbbckp > 0) {
		for	(i=0;i<nbbckp;i+=1){
			//printf("%4d: %x (%c)\n",i,buffer[i],buffer[i]);
			machine->WriteMem(vbackup+i,1,(buffer[i]));
		}		
	}
}

// Gestion de l'adressage des UserThreads

void
AddrSpace::NumThreadsUp ()
{
    SemNumThreads->P ();
    NumThreads++;
    SemNumThreads->V ();
}

void
AddrSpace::NumThreadsDown ()
{
    SemNumThreads->P ();
    NumThreads--;
    SemNumThreads->V ();
}

int
AddrSpace::GetNbThreads ()
{
    int num;
    SemNumThreads->P ();
    num = NumThreads;
    SemNumThreads->V ();
    return num;
}

int AddrSpace::GetNbPiles() {
	return nombrePiles;
}

#endif // CHANGED
